package cn.hiapi.core.basic.query;

import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Sort;

import java.io.Serializable;
import java.util.*;
import java.util.function.BiConsumer;

/**
 * @author AdinZ
 * @date 2021/1/20 2:07 下午
 */
public class QueryWrapper implements Serializable {
    private final Map<String, ConditionValue> map = new IdentityHashMap<>();
    private Sort sort = Sort.unsorted();
    private QueryWrapper orWrapper;

    public QueryWrapper eq(String column, Object value) {
        return this.of(Keyword.EQ, column, value);
    }

    public QueryWrapper in(String column, Object... value) {
        if (value != null)
            return of(Keyword.IN, column, Arrays.asList(value));
        return this;
    }

    public <T> QueryWrapper in(String column, Collection<T> value) {
        if (value != null && !value.isEmpty())
            return of(Keyword.IN, column, value);
        return this;
    }


    public QueryWrapper notIn(String column, Object... value) {
        return of(Keyword.NOTIN, column, value);
    }

    public QueryWrapper like(String column, Object value) {
        return this.of(Keyword.LIKE, column, value);
    }

    public QueryWrapper ne(String column, Object value) {
        return this.of(Keyword.NE, column, value);
    }

    public QueryWrapper gt(String column, Object value) {
        return this.of(Keyword.GT, column, value);
    }

    public QueryWrapper ge(String column, Object value) {
        return this.of(Keyword.GE, column, value);
    }

    public QueryWrapper lt(String column, Object value) {
        return this.of(Keyword.LT, column, value);
    }

    public QueryWrapper le(String column, Object value) {
        return this.of(Keyword.LE, column, value);
    }

    public QueryWrapper isNull(String column) {
        return this.of(Keyword.IS_NULL, column, "isNull");
    }

    public QueryWrapper between(String column, Long... value) {
        if (value == null || value.length == 0) {
            return this;
        } else if (value.length != 2)
            throw new RuntimeException(" between 参数 数组不正确:" + Arrays.toString(value));
        return this.of(Keyword.BETWEEN, column, value);
    }

    public QueryWrapper between(String column, Object... value) {
        if (value == null || value.length == 0) {
            return this;
        } else if (value.length != 2)
            throw new RuntimeException(" between 参数 数组不正确:" + Arrays.toString(value));
        return this.of(Keyword.BETWEEN, column, value);
    }

    public <T> QueryWrapper between(String column, Collection<T> value) {
        if (value == null || value.isEmpty()) {
            return this;
        } else if (value.size() != 2)
            throw new RuntimeException(" between 参数 数组不正确:" + value);
        return this.of(Keyword.BETWEEN, column, value);
    }

    public QueryWrapper isNotNull(String column) {
        return this.of(Keyword.IS_NOT_NULL, column, "isNotNull");
    }


    public QueryWrapper or(QueryWrapper wrapper) {

        this.orWrapper = wrapper;
        return this;
    }

    public QueryWrapper getOr() {
        return orWrapper == null ? new QueryWrapper() : orWrapper;
    }

    public QueryWrapper sort(Sort sort) {
        this.sort = sort;
        return this;
    }

    public QueryWrapper sort(Sort.Direction direction, String... properties) {
        this.sort = Sort.by(direction, properties);
        return this;
    }


    public Sort getSort() {
        return sort;
    }

    public QueryWrapper merge(QueryWrapper wrapper) {
        this.map.putAll(wrapper.map);
        return this ;
    }

    /**
     * 条件
     *
     * @param type   条件类型
     * @param column 数据库键值
     * @param value
     * @return
     */
    public QueryWrapper of(Keyword type, String column, Object value) {
        if (value instanceof String) {
            if (StringUtils.isEmpty(value.toString())) {
                return this;
            }
        }
        if (value != null) {
            this.map.put(column, new ConditionValue(type, value));
        }
        return this;
    }

    private final Set<String> groupBys = new HashSet<>();

    public QueryWrapper groupBy(String... columns) {
        groupBys.addAll(Arrays.asList(columns));
        return this;
    }

    public Set<String> groupBy() {
        return groupBys;
    }


    private final List<GroupBy> groupColumns = new ArrayList<>();


    public List<GroupBy> groupColumns() {
        return this.groupColumns;
    }

    public QueryWrapper avg(String column) {
        groupColumns.add(new GroupBy(column, GroupKey.AVG));
        return this;
    }

    public QueryWrapper sum(String column) {
        groupColumns.add(new GroupBy(column, GroupKey.SUM));
        return this;
    }

    public QueryWrapper count(String column) {
        groupColumns.add(new GroupBy(column, GroupKey.COUNT));
        return this;
    }

    public QueryWrapper max(String column) {
        groupColumns.add(new GroupBy(column, GroupKey.MAX));
        return this;
    }

    public QueryWrapper min(String column) {
        groupColumns.add(new GroupBy(column, GroupKey.MIN));
        return this;
    }

    public boolean isEmpty() {
        return map.isEmpty();
    }

    public void forEach(BiConsumer<String, ConditionValue> action) {
        this.map.forEach(action);
    }


    @Getter
    public static class ConditionValue  implements Serializable {
        private final Keyword keyword;
        private final Object value;

        public ConditionValue(Keyword keyword, Object value) {
            this.keyword = keyword;
            this.value = value;
        }
    }

    public record GroupBy(String column, GroupKey key) {
    }


    public enum GroupKey {
        SUM, COUNT, MIN, MAX, AVG
    }

    public enum Keyword {
        IN("IN"),
        NOTIN("NOT IN"),
        LIKE("LIKE"),
        EQ("="),
        NE("<>"),
        GT(">"),
        GE(">="),
        LT("<"),
        LE("<="),
        IS_NULL("IS NULL"),
        IS_NOT_NULL("IS NOT NULL"),
        BETWEEN("between");


        Keyword(String name) {

        }
    }
}
